package com.study.web.action;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

/**
* @Package com.study.web.action
* @ClassName: TestOrderSytemToSendoutGoodsSystem
* @Description: 测试订单系统 向 发货系统 发送订单
* @author yaojinshan
* @date 2019年1月21日 下午10:05:55
* @version 1.0
*/
public class TestOrderSytemToSendoutGoodsSystem {
	@Autowired
	private OrderSytemToSendoutGoodsSystemDaoImpl daoImpl;
	/**
	* @Description:初级写法： 普通写法 发送订单到发货系统
	* 备注：未考虑到异常情况导致数据有效性不能保证  未考虑到数据库链接占用导致数据库链接长时间占用  未考虑到重复提交带来的多次发货
	* @Title: sendOrderByCommon
	* @param orderId   
	* @author yaojinshan
	* @date 2019年1月21日 下午10:09:12
	* @throws
	 */
	public void sendOrderByCommon(String orderId){
		//1.获取订单详情
		TestOrder testOrder = daoImpl.getOrder();
		//2.调用发货系统 会占用数据库链接 如果远程服务长时间不响应 就会一致占用当前的数据库链接 daoImpl  未考虑到重复提交带来的多次发货
		String flag = TestOrder.callbackOrderToSendoutGoodsStatus(testOrder);
		testOrder.setOrderStatus(flag);
		//3.更新订单状态 如果发生了其他异常 更新了订单状态 就不能保证数据有效性
		daoImpl.updateOrder(testOrder);
	}
	
	/**
	* @Description:中级写法： 通过事务来控制发送订单到发货系统
	* 备注：考虑到异常情况导致数据有效期可以保证  未考虑到数据库链接占用导致数据库链接长时间占用  未考虑到重复提交带来的多次发货
	* @Title: sendOrderByTransaction
	* @param orderId   
	* @author yaojinshan
	* @date 2019年1月21日 下午10:09:36
	* @throws
	 */
	@Transactional
	public void sendOrderByTransaction(String orderId){
		//1.获取订单详情
		TestOrder testOrder = daoImpl.getOrder();
		//2.调用发货系统 会占用数据库链接 如果远程服务长时间不响应 就会一致占用当前的数据库链接 daoImpl  未考虑到重复提交带来的多次发货
		String flag = TestOrder.callbackOrderToSendoutGoodsStatus(testOrder);
		testOrder.setOrderStatus(flag);
		//3.使用了 @Transactional 标签 解决了异常回滚的问题
		daoImpl.updateOrder(testOrder);
	}
	
	@Autowired
	private TransactionTemplate template;
	/**
	* @Description: 高级写法： 通过TransactionTemplate发送订单到发货系统
	* 备注：考虑到异常情况导致数据有效期可以保证  考虑到数据库链接占用 数据库链接有效使用率  未考虑到重复提交带来的多次发货
	* @Title: sendOrderByTransactionTemplate
	* @param orderId   
	* @author yaojinshan
	* @date 2019年1月21日 下午10:11:13
	* @throws
	 */
	public void sendOrderByTransactionTemplate(String orderId){
		//1.获取订单详情
		final TestOrder testOrder = daoImpl.getOrder();
		//模拟重复提交启动 六个线程
		for(int i = 0;i < 6;i++){
			new Thread(new TestOrderMultithread(testOrder)).start();
		}
	}
	/**
	* @Description: 高级进阶：通过TransactionTemplateAndVersion发送订单到发货系统
	* 备注：考虑到异常情况导致数据有效期可以保证  考虑到数据库链接占用 数据库链接有效使用率  使用乐观锁解决重复提交带来的多次发货
	* @Title: sendOrderByTransactionTemplateAndVersion
	* @param orderId   
	* @author yaojinshan
	* @date 2019年1月21日 下午10:11:53
	* @throws
	 */
	public void sendOrderByTransactionTemplateAndVersion(String orderId){
		//1.获取订单详情
		final TestOrder testOrder = daoImpl.getOrder();
		//模拟重复提交启动 六个线程
		for(int i = 0;i < 6;i++){
			new Thread(new TestOrderMultithread2(testOrder)).start();
		}
	}
	
	/**
	* @Description:测试订单线程类 模拟重复提交
	* @Package com.study.web.action
	* @ClassName: TestOrderMultithread
	* @author yaojinshan
	* @date 2019年1月21日 下午10:50:36
	* @version 1.0
	 */
	class TestOrderMultithread implements Runnable{
		private TestOrder testOrder;
		public TestOrderMultithread(TestOrder testOrder) {
			this.testOrder = testOrder;
		}
		public void run() {
			template.execute( new TransactionCallback<Object>(){
				public Object doInTransaction(TransactionStatus status) {
					testOrder.setOrderStatus("已发送给发货系统");
					daoImpl.updateOrder(testOrder);
					return null;
				}
			});
			//2.使用 TransactionTemplate 会释放当前的数据连接daoImpl 确保数据库连接的有效使用率  未考虑到重复提交带来的多次发货
			final String flag = TestOrder.callbackOrderToSendoutGoodsStatus(testOrder);
			template.execute( new TransactionCallback<Object>(){
				public Object doInTransaction(TransactionStatus status) {
					testOrder.setOrderStatus(flag);
					daoImpl.updateOrder(testOrder);
					return null;
				}
			});
		}
	}

	/**
	 * @Description:测试订单线程类2 模拟重复提交 解决多次发货问题
	 * @Package com.study.web.action
	 * @ClassName: TestOrderMultithread2
	 * @author yaojinshan
	 * @date 2019年1月21日 下午10:50:36
	 * @version 1.0
	 */
	class TestOrderMultithread2 implements Runnable{
		private TestOrder testOrder;
		public TestOrderMultithread2(TestOrder testOrder) {
			this.testOrder = testOrder;
		}
		public void run() {
			Boolean exeFlag = template.execute( new TransactionCallback<Boolean>(){
				public Boolean doInTransaction(TransactionStatus status) {
					//假设数据库订单的初始version为0
					testOrder.setVersion(0);
					//当第一次请求执行完毕之后 会更新了订单版本号 （利用乐观锁原理） 所以 后续时重复提交导致的请求 因为所带的初始版本号0和数据库的新的版本号不一致 导致不会更新成功
					testOrder.setOrderStatus("已发送给发货系统");
					return 1 == daoImpl.updateOrderByVersion(testOrder, testOrder.getVersion() + 1);
				}
			});
			//2.使用数据库乐观锁 只处理更新订单成功的那一次请求
			if(exeFlag){
				//3.使用 TransactionTemplate 会释放当前的数据连接daoImpl 确保数据库连接的有效使用率  
				final String flag = TestOrder.callbackOrderToSendoutGoodsStatus(testOrder);
				template.execute( new TransactionCallback<Boolean>(){
					public Boolean doInTransaction(TransactionStatus status) {
						//3.根据响应标志更新订单状态
						testOrder.setOrderStatus(flag);
						daoImpl.updateOrder(testOrder);
						return null;
					}
				});
			}else{
				System.out.println("订单已被处理。。。。。。");
			}
		}
	}
}
/**
* @Description: 订单系统到发货系统的dao类
* @Package com.study.web.action
* @ClassName: OrderSytemToSendoutGoodsSystemDaoImpl
* @author yaojinshan
* @date 2019年1月21日 下午10:19:13
* @version 1.0
 */
class OrderSytemToSendoutGoodsSystemDaoImpl{
	
	public void updateOrder(TestOrder testOrder){
		//
	} 
	/**
	* @Description: 通过订单版本号更新订单  并将新的订单版本号赋值给订单
	* @Title: updateOrderByVersion
	* @param testOrder
	* @param newOrderVersion   
	* @author yaojinshan
	* @date 2019年1月21日 下午10:57:19
	* @throws
	 */
	@SuppressWarnings("unused")
	public Integer updateOrderByVersion(TestOrder testOrder,int newOrderVersion){
		//假设更新成功 返回更新影响的行数 一版订单号时订单的唯一标识  也就是影响了1行
		//底层的sql demo类似: update bus_order bo set bo.version = #{newOrderVersion} where bo.order_id = #{testOrder.orderId} and  bo.version = #{testOrder.version}
		if(true){
			return 1;
		}else{
			return 0;
		}
	}
	
	public TestOrder getOrder(){
		TestOrder testOrder = new TestOrder();
		testOrder.setOrderId("1");
		testOrder.setOrderStatus("0");
		testOrder.setOrderTime("");
		return testOrder;
	}
	
}
/**
* @Description:测试订单实体类
* @Package com.study.web.action
* @ClassName: TestOrder
* @author yaojinshan
* @date 2019年1月21日 下午10:21:52
* @version 1.0
 */
class TestOrder{
	private String orderId;
	private String orderTime;
	private String orderStatus;
	private Integer version;
	public String getOrderId() {
		return orderId;
	}
	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}
	public String getOrderTime() {
		return orderTime;
	}
	public void setOrderTime(String orderTime) {
		this.orderTime = orderTime;
	}
	public String getOrderStatus() {
		return orderStatus;
	}
	public void setOrderStatus(String orderStatus) {
		this.orderStatus = orderStatus;
	}
	public Integer getVersion() {
		return version;
	}
	public void setVersion(Integer version) {
		this.version = version;
	}
	/**
	* @Description:  回调订单系统到发货系统的状态
	* @Title: callbackOrderToSendoutGoodsStatus
	* @return   
	* @author yaojinshan
	* @date 2019年1月21日 下午10:24:47
	* @throws
	 */
	public static String callbackOrderToSendoutGoodsStatus(TestOrder testOrder){
		// 5：已发货
		return "已发货成功";
	}
}
